#include <iostream>
#include "Task.h"
#include <time.h>
#include <sys/trace.h>
#include <sstream>

Task::Task() {
	// events
	this->PREEMPT_EVN		="Preempt";
	this->MISS_DEADLINE_EVN	="Miss its Deadline";
	this->START_EVN			="Start";
	this->STOP_EVN			="Stop";
	this->RESUM_EVN			="resume";

	//Properties ( uninitialized Task)
	this->id				= -1;
	this->executionTime		= -1;
	this->period 			= -1;// how many unites this task is allowed to finish its execution
	this->priority			= -1;
	this->clockCounter		= 0;// how many unites are gone so far
	this->exeCycls			= 0;
	this->status			= "BLOCKED";
	this->startTime.unit	= 0;// represent start relative time to our ruler (scale)
	this->startTime.subUnit	= 0;
	this->stopTime.unit		= 0; // represent stop relative time to our ruler (scale)
	this->stopTime.subUnit	= 0;

	// flags
	this->done				=false;
	this->stopFlag			=false;
	this->running			=false;
	this->success			=true;// to indicate that the task success to finish its execution during its period

	// tools
	Scale *scale			=NULL; // our ruler to help us mouser the task execution

}

// to initialize the tasks
void Task::InitTask(int pExecutionTime, int pTaskPeriod, Scale *s, int id) {

	this->id			=id;
	this->executionTime = pExecutionTime;
	this->period		= pTaskPeriod;
	this->scale			=s;
	//this->tMng			=tm;
	sem_init(&this->semId, 0, 0);
}


void Task::mark(string event) {

	stringstream tmp;
	string str;
	char tn[100];
	pthread_getname_np(pthread_self(),tn,sizeof(tn));

	// set START or STOP relative times
	if (event == this->START_EVN) {
		tmp <<"START TASK#" << this->id<<", "<<tn;
		str=tmp.str();
		// add the event to the execution stack
		this->addExEvent(str, scale->getRelTime());
		TraceEvent(_NTO_TRACE_INSERTUSRSTREVENT, 700, str.c_str());
		this->startTime = scale->getUniTime();
	}
	if (event == this->STOP_EVN) {
		tmp <<"STOP TASK#" << this->id<<", "<<tn;
		str= tmp.str();
		// add the event to the execution stack
		this->addExEvent(str, scale->getRelTime());
		TraceEvent(_NTO_TRACE_INSERTUSRSTREVENT, 750, str.c_str());
		this->stopTime = scale->getUniTime();
	}

	if (event == this->PREEMPT_EVN) {
		tmp << "PREEMPT TASK#" << this->id<<", "<<tn;
		str= tmp.str();
		// add the event to the execution stack
		this->addExEvent(str, scale->getRelTime());
		TraceEvent(_NTO_TRACE_INSERTUSRSTREVENT, 800, str.c_str());
	}

	if (event == this->RESUM_EVN) {
		tmp << "RESUME TASK#" << this->id<<", "<<tn;
		str= tmp.str();
		// add the event to the execution stack
		this->addExEvent(str, scale->getRelTime());
		TraceEvent(_NTO_TRACE_INSERTUSRSTREVENT, 850, str.c_str());
	}


	if (event == this->MISS_DEADLINE_EVN) {
		tmp << "MISS DEADLINE TASK#" << this->id<<", "<<tn;
		str= tmp.str();
		// add the event to the execution stack
		this->addExEvent(str, scale->getRelTime());
		TraceEvent(_NTO_TRACE_INSERTUSRSTREVENT, 900, str.c_str());
	}
}

void Task::addExEvent(string e, timespec t) {
	ExEvent event;
	event.event = e;
	event.time = t;
	this->exeQueue.push(event);
}

int Task::checkPeriod() {

	//check next Period
	if(this->scale->nextPeriod != 0 && this->scale->getUniTime().unit>= this->scale->nextPeriod)this->stopFlag=true;

	//first check stop flag
	if(this->stopFlag){
		this->running=false;
		this->stopFlag=false;
		this->mark(this->PREEMPT_EVN);
		this->status="BLOCKED";
		if(sem_wait(&this->semId)==-1)cout <<"problem to wait in checkPeriod";
		this->running=true;
		this->mark(this->RESUM_EVN);
	}

	// definitions
	unitTime currentTime, executionTime;
	currentTime = this->scale->getUniTime();

	// calculate execution period so far
	executionTime.subUnit = currentTime.subUnit - this->startTime.subUnit;
	executionTime.unit = currentTime.unit - this->startTime.unit;
	if (executionTime.subUnit < 0) {
		executionTime.unit--;
		executionTime.subUnit += 5000000;
	}

	// check the execution time against to task's period
	if (executionTime.unit > this->period)
		this->success = false;
	else if (executionTime.unit == this->period && executionTime.subUnit > 0)
		this->success = false;

	// if status is fail we should do something like preempt
	if (!this->success) {
		// add event
		this->mark(this->MISS_DEADLINE_EVN);

		//terminate execution
		return 0;
	}
	return 1;
}
